package sf.dsl.example;

import sf.common.exception.SmallOrmException;
import sf.core.DBField;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.SQLParameter;
import sf.database.meta.ColumnMapping;
import sf.database.util.SQLUtils;
import sf.tools.ArrayUtils;
import sf.tools.StringUtils;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class DSLMethod {
    public static SimpleField simpleField(List<SimpleTable> fromTables, DBField property) {
        if (property != null) {
            SimpleField simpleField = null;
            boolean find = false;
            for (SimpleTable simpleTable : fromTables) {
                if (simpleTable.tableMapping != null &&
                        simpleTable.tableMapping.getSchemaMap().containsKey(property)) {
                    if (!find) {
                        find = true;
                        simpleField = simpleTable.c(property);
                    } else {
                        throw new SmallOrmException("multiple tables found!");
                    }
                }
            }
            if (!find) {
                throw new SmallOrmException("no table found!");
            }
            return simpleField;
        }
        return null;
    }

    /**
     * 设置字段的表别名
     * @param field
     * @return
     */
    public static String getColumnAlias(SimpleField field, DBDialect dialect) {
        String sql = "";
        if (field != null) {
            if (dialect != null) {
                sql = dialect.wrapKeyword(field.getName());
            } else {
                sql = field.getName();
            }
            if (field.getTable() != null && StringUtils.isNotBlank(field.getTable().getTableAlias())) {
                sql = field.getTable().getTableAlias() + "." + sql;
            }
            if (StringUtils.isNotBlank(field.getAlias())) {
                String alias = field.getAlias();
                if (dialect != null) {
                    alias = dialect.wrapKeyword(alias);
                }
                sql = sql + " as " + alias;
            }
        }
        return sql;
    }

    public static String getTableAlias(SimpleTable c, DBDialect dialect) {
        String tableName = c.tableName;
        if (dialect != null) {
            tableName = dialect.wrapKeyword(tableName);
        }
        if (StringUtils.isNotBlank(c.getTableAlias())) {
            String alias = c.getTableAlias();
            if (dialect != null) {
                alias = dialect.wrapKeyword(alias);
            }
            tableName = tableName + " as " + alias;
        }
        return tableName;
    }

    public static SimpleField[] toArray(String... column) {
        if (ArrayUtils.isNotEmpty(column)) {
            SimpleField[] fields = new SimpleField[column.length];
            for (int i = 0; i < column.length; i++) {
                String sf = column[i];
                SimpleField simpleField = new SimpleField();
                simpleField.setName(sf);
                fields[i] = simpleField;
            }
            return fields;
        }
        return new SimpleField[]{};
    }

    public static ColumnMapping[] columnList(StringBuilder sb, List<SQLParameter> list, SimpleField[] fields,
                                             AtomicBoolean mustTableAlias, boolean useTablePrefix, DBDialect dialect) {
        ColumnMapping[] columns = null;
        if (fields != null) {
            int count = fields.length;
            columns = new ColumnMapping[count];
            if (count > 1) {
                sb.append('(');
            }
            for (int i = 0; i < count; i++) {
                SimpleField field = fields[i];
                columns[i] = field.getColumnMapping();
                if (i > 0) {
                    sb.append(',');
                }
                field.toConditionSql(sb, list, mustTableAlias, useTablePrefix, dialect);
            }
            if (count > 1) {
                sb.append(')');
            }
        }
        return columns;
    }

    /**
     * 添加占位符
     * @param sql
     * @param operator
     * @param value
     * @param escape
     */
    public static int addPlaceholder(StringBuilder sql, DBOperator operator, int columnSize, Object value, ValueType valueType, String escape) {
        int count = 0;
        switch (operator) {
            case LESS:
            case GREAT:
            case EQUALS:
            case LESS_EQUALS:
            case GREAT_EQUALS:
            case LIKE:
            case NOT_LIKE:
            case NOT_EQUALS: {
                count = placeholderMultiple(sql, 1, columnSize);
                break;
            }
            case IS_NULL:
            case IS_NOT_NULL:
                break;
            case LIKE_ESCAPE:
            case NOT_LIKE_ESCAPE:
                if (escape == null) {
                    escape = "/";
                }
                sql.append(' ').append(SQLUtils.placeholder()).append(" escape '").append(escape).append('\'');
                count = 1;
                break;
            case IN:
            case NOT_IN:
                sql.append('(');
                count = addPlaceholderByValue(sql, columnSize, null, value, valueType);
                sql.append(')');
                break;
            case BETWEEN:
            case NOT_BETWEEN:
                sql.append(SQLUtils.placeholder()).append(" and ").append(SQLUtils.placeholder());
                count = 2;
                break;
            case NOTHING:
                count = addPlaceholderByValue(sql, columnSize, null, value, valueType);
                break;
            default:
                break;
        }
        return count;
    }

    /**
     * @param sql
     * @param columnSize 列数量
     * @param valueSize  值数量
     * @param value
     * @param valueType
     */
    public static int addPlaceholderByValue(StringBuilder sql, int columnSize, Integer valueSize, Object value, ValueType valueType) {
        int count = 0;
        switch (valueType) {
            case noValue:
                break;
            case singleValue:
                sql.append(SQLUtils.placeholder());
                count = 1;
                break;
            case arrayValue: {
                int length = Array.getLength(value);
                if (valueSize != null && valueSize > 0 && valueSize < length) {
                    length = valueSize;
                }
                count = placeholderMultiple(sql, getTupleCount(length, columnSize), columnSize);
                break;
            }
            case listValue: {
                int length = ((Collection<?>) value).size();
                if (valueSize != null && valueSize > 0 && valueSize < length) {
                    length = valueSize;
                }
                count = placeholderMultiple(sql, getTupleCount(length, columnSize), columnSize);
                break;
            }
            default:
                break;
        }
        return count;
    }

    /**
     * 获取元组个数
     * @param valueSize
     * @param columnSize
     * @return
     */
    public static int getTupleCount(int valueSize, int columnSize) {
        if (columnSize > 0) {
            return valueSize / columnSize;
        }
        return 0;
    }

    /**
     * @param sql
     * @param group      组数
     * @param columnSize 列长度 大于1才有意义
     * @return 返回占位符个数
     */
    public static int placeholderMultiple(StringBuilder sql, int group, int columnSize) {
        for (int k = 0; k < group; k++) {
            if (k > 0) {
                sql.append(',');
            }
            if (columnSize > 1) {
                sql.append('(');
            }
            for (int i = 0; i < columnSize; i++) {
                if (i > 0) {
                    sql.append(',');
                }
                sql.append(SQLUtils.placeholder());
            }
            if (columnSize > 1) {
                sql.append(')');
            }
        }
        return group * columnSize;
    }

    /**
     * @param parameters
     * @param columns
     * @param value
     * @param valueType
     */
    public static void setSqlParameters(List<SQLParameter> parameters, int columnSize, ColumnMapping[] columns, Integer valueSize, Object value, ValueType valueType) {
        ColumnMapping column = null;
        if (columns != null && columns.length > 0) {
            column = columns[0];
        }
        switch (valueType) {
            case noValue:
                break;
            case singleValue:
                parameters.add(new SQLParameter(value, column));
                break;
            case arrayValue: {
                int length = Array.getLength(value);
                if (valueSize != null && valueSize > 0 && valueSize < length) {
                    length = valueSize;
                }
                for (int i = 0; i < length; i++) {
                    Object o = Array.get(value, i);
                    setMultipleValues(parameters, columnSize, i, o, columns, column);
                }
                break;
            }
            case listValue: {
                int length = ((Collection<?>) value).size();
                if (valueSize != null && valueSize > 0 && valueSize < length) {
                    length = valueSize;
                }
                int i = 0;
                for (Object obj : (Collection<?>) value) {
                    setMultipleValues(parameters, columnSize, i, obj, columns, column);
                    i++;
                    if (i >= length) {
                        break;
                    }
                }
                break;
            }
            default:
                break;
        }
    }

    public static void setMultipleValues(List<SQLParameter> parameters, int columnSize, int i, Object o,
                                         ColumnMapping[] columns, ColumnMapping column) {
        if (columnSize > 1) {
            ColumnMapping cm = null;
            int k = i % columnSize;
            if (columns != null && columns.length > k) {
                cm = columns[k];
            }
            parameters.add(new SQLParameter(o, cm));
        } else {
            parameters.add(new SQLParameter(o, column));
        }
    }
}
